﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.ComponentModel;

namespace HWR_Ants.Classes.Obsolete_2
{
    class Tour
    {
        TSP_File tourFile;
        Options currentOptions;
        DistanceTable distanceTable;
        PheromonTable pheromonTable;
        BackgroundWorker[] bgs;

        // LISTS
        List<Ant> ants;
        IList<Point> listOfCities;
        SortedList<string, Point> sortedListOfCities;
        List<int> bestWay;
        double bestDistance;
        int currentAntBW = 0;
        double[,] temp;
        int bw_finished;

        private Tour(Options options)
        {
            currentOptions = options;
            tourFile = null;
            sortedListOfCities = new SortedList<string, Point>();
            Tools.Tools.ActiveCurrentOptions = options;
            bestDistance = 0;
            bestWay = new List<int>();
            bgs = new BackgroundWorker[options.AntsCounter];
            for (int i = 0; i < options.AntsCounter; i++)
            {
                bgs[i] = new BackgroundWorker();
                bgs[i].WorkerReportsProgress = true;
                bgs[i].DoWork += new DoWorkEventHandler(bw_DoWork);
                bgs[i].RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
            }
        }

        public Tour(TSP_File tspFile, Options options) : this(options)
        {
            tourFile = tspFile;
            sortedListOfCities = tspFile.Coordinates;
            listOfCities = sortedListOfCities.Values;
            distanceTable = new DistanceTable(listOfCities);
            pheromonTable = new PheromonTable(listOfCities,currentOptions.Parameter.Tau);
            Tools.Tools.ActiveDistanceTable = distanceTable;
            Tools.Tools.ActivePheromonTable = pheromonTable;
            initializeAnts();
        }

        public Tour(IList<Point> loc, Options options) : this(options)
        {
            listOfCities = loc;
            distanceTable = new DistanceTable(listOfCities);
            pheromonTable = new PheromonTable(listOfCities, currentOptions.Parameter.Tau);
            Tools.Tools.ActiveDistanceTable = distanceTable;
            Tools.Tools.ActivePheromonTable = pheromonTable;
            initializeAnts();
        }

        private void initializeAnts()
        {
            ants = new List<Ant>();
            if (currentOptions.AntsCounter >= listOfCities.Count)
            {
                int j = 0;
                for (int i = 0; i < currentOptions.AntsCounter; i++)
                {
                    if (j == listOfCities.Count - 1)
                        j = 0;

                    ants.Add(new Ant(j, listOfCities));
                    j++;
                }
            }
            else
            {
                for (int i = 0; i < currentOptions.AntsCounter; i++)
                {
                    ants.Add(new Ant(i, listOfCities));
                }
            }
        }

        public void StartTour()
        {
            bool finished = false; 
            int i = 0;
            bw_finished = 0;
            Tools.Tools.ProbabilityTable = calculateProbability();
            while (i < currentOptions.IterationCounter)
            {

                PheromonTable pt = new PheromonTable(listOfCities, 0);
                temp = pt.PheromonMatrix;

                while (!finished)
                {
                    for (int j = 0; j < ants.Count; j++)
                    {
                        bgs[j].RunWorkerAsync();
                    }

                    /*
                    for (int j = 0; j < ants.Count; j++)
                    {
                        // 1 müsste Parameter Pheromon Update sein.
                        // Verdunstung mit einbeziehen
                        temp[ants[j].CityTo, ants[j].CityFrom] += (1 / ants[j].CurrentDistanceWalked);
                        temp[ants[j].CityFrom, ants[j].CityTo] += (1 / ants[j].CurrentDistanceWalked);
                    }
                    */
                    while (bw_finished != bgs.Length)
                        Application.DoEvents();


                    finished = true;
                    bw_finished = 0;
                    currentAntBW = 0;
                }

                for (int j = 0; j < ants.Count; j++)
                {
                    if (bestDistance > ants[j].DistanceWalked || bestDistance == 0)
                    {
                        bestDistance = ants[j].DistanceWalked;
                        bestWay = ants[j].ListOfWalkedCities;
                    }
                }
                initializeAnts();
                Tools.Tools.ActivePheromonTable.UpdatePheromonStatus(temp);
                finished = false;
                i++;
                Tools.Tools.ProbabilityTable = calculateProbability();
            }

            System.Windows.Forms.MessageBox.Show(bestDistance.ToString());
        }

        private double[,] calculateProbability()
        {
            double[,] dtMatrix = Tools.Tools.ActiveDistanceTable.DistanceMatrix;
            double[,] ptMatrix = Tools.Tools.ActivePheromonTable.PheromonMatrix;

            int alpha = Tools.Tools.ActiveCurrentOptions.Parameter.Alpha;
            int beta = Tools.Tools.ActiveCurrentOptions.Parameter.Beta;

            double[,] ret = new double[listOfCities.Count,listOfCities.Count];

            for (int i = 0; i < listOfCities.Count; i++)
            {
                for (int j = 0; j < listOfCities.Count; j++)
                {
                    double tau = ptMatrix[i, j];
                    double distance = dtMatrix[i, j];
                    ret[i,j] = calculateTauDistance(alpha, beta, tau, distance); 
                }
            }

            return ret;
        }

        private double calculateTauDistance(int alpha, int beta, double tau, double distance)
        {
            return Math.Pow(tau, alpha) * Math.Pow(1/distance, beta);
        }

        private void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            bool finished = false;
            int j = currentAntBW;
            currentAntBW++;
            while (!finished)
            {
                finished = ants[j].WalkToNextCity();
                temp[ants[j].CityTo, ants[j].CityFrom] += (1 / ants[j].CurrentDistanceWalked);
                temp[ants[j].CityFrom, ants[j].CityTo] += (1 / ants[j].CurrentDistanceWalked);
            }
            // bw_RunWorkerCompleted(sender, new RunWorkerCompletedEventArgs(null, null, false));
            // bgs[j].IsBusy = false;

            e.Result = true;
            return;
        }

        private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bw_finished++;
        }
    }
}
